package nl.scalasim
package actors
package citizenactivities

import scala.actors.Actor
import nl.scalasim.util.Accumulator
import nl.scalasim.actors.traits.{SimulationActor, StorageActor, HarvestingActor}

/**
 * Specific AtWork currentActivity for citizens working at an
 * IntermediateStorage actor.
 *
 * When working at an IntermediateStorage actor, the citizen takes the
 * following actions:
 * - if the storage is full: do nothing
 * - if the storage has room for goods: find a SurplusStorage actor that
 *   has a surplus; go to that actor, retrieve goods, go back to the storage
 *   and store the goods.    
 *
 * @author Yigal Duppen
 * @since Oct 28, 2010
 */
class AtWorkAtStorageActivity(val citizen: Citizen) extends Activity {

  private var accumulator: Accumulator = _
  private var shouldGoHome: Boolean = _
  private val workplace = citizen.workplace.get.asInstanceOf[StorageActor]

  private var currentLocation = 'AtStorage
  private var currentTarget: Option[SimulationActor] = None


  def activate = {
    accumulator = new Accumulator(citizen.ticksAtWork)
    shouldGoHome = false
  }

  override def handleFound(a: SimulationActor) = {
    log.info(citizen + " found a place with surplus goods: " + a)
    currentTarget = Some(a)
  }


  override def handleArrived(target: SimulationActor) = {
    if (target == workplace) {
      log.info(citizen + " is back at storage")
      currentLocation = 'AtStorage
      currentTarget = None
      dumpGoods
    }
    if (Some(target) == currentTarget) {
      log.info(citizen + " reached the harvester")
      currentLocation = 'AtHarvester
    }
  }


  def handleAct = {
    accumulator.overflow match {
      case 0 => accumulator = accumulator.accumulate
      case 1 => shouldGoHome = true
    }

    (currentLocation, currentTarget, shouldGoHome) match {
      case ('AtStorage, _, true) => {
        goHome
      }

      case ('AtStorage, None, _) => {
        findTemporaryStorage
      }

      case ('AtStorage, Some(target), _) => {
        goToWork(target)
      }

      case ('Moving, _, _) => {
        log.info("walking...")
        done()
      }

      case ('AtHarvester, _, _) => {
        retrieveFromTemporaryStorage
      }
    }
  }

  private def findTemporaryStorage = {
    log.info("Finding out if our storage needs extra goods")
    workplace ! GetState
    react {
      case a:ActorState if a.stored.find( _._2.remaining >= citizen.carryingCapacity) != None => {
        log.fine("Storage can take more goods, let's find them")
        val requiredGoods = a.stored collect {case entry if entry._2.remaining > citizen.carryingCapacity => { entry._1 }}

        citizen.addressBook.guildmaster ! FindGoods(citizen, requiredGoods map { (_, citizen.carryingCapacity) } toList )
        done()
      }

      case _ => {
        // nothing to do
        log.info("Storage is full")
        done()
      }
    }
  }

  private def retrieveFromTemporaryStorage = {
    log.info(citizen + " is retrieving goods from " + currentTarget)

    currentTarget ! Retrieve(harvester.goods, citizen.carryingCapacity)
    react {
      case Retrieved(goods, amount) => {
        log.info(citizen + " retrieved " + amount + " " + goods + " from " + currentTarget)
        citizen.carrying = Some((goods, amount))

        // return to workplace
        currentLocation = 'Moving
        citizen.addressBook.choreographer ! MoveTo(citizen, workplace)
        done()
      }
    }
  }

  private def dumpGoods: Unit = {
    val (goods, amount) = citizen.carrying.get
    citizen.carrying = None
    workplace ! Store(goods, amount)
    react {
      case Stored(goods, amount) => log.info(citizen + " stored " + amount + " " + goods + " at " + workplace)
    }
  }
  
  private def goHome: Unit = {
    log.info("Work time is over, moving to " + citizen.house)
    citizen.currentActivity = citizen.commuting
    citizen.commuting.nextActivity = citizen.atHome
    citizen.addressBook.choreographer ! MoveTo(citizen, citizen.house)
    done()
  }

  private def goToWork(target: SimulationActor): Unit = {
    log.info("We have a target, let's go there")
    currentLocation = 'Moving
    citizen.addressBook.choreographer ! MoveTo(citizen, target)
    done()
  }

}